/*
 * JANN - a Java toolkit for creating arbitrary Artificial Neural Networks.
 * 
 * Copyright (c) 2009 Matthijs Snel
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.jann;

import java.io.Serializable;
import java.util.List;
import java.util.ArrayList;

public class Layer implements Serializable {

	private static final long serialVersionUID = 234445291114843804L;
	
	protected List<Neuron> neurons;
	protected List<LayerLink> incoming, outgoing;

	public Layer() {
		neurons = new ArrayList<Neuron>();
		incoming = new ArrayList<LayerLink>();
		outgoing = new ArrayList<LayerLink>();
	}
	
	public Layer( int numNeurons ) {
		this(numNeurons, null);
	}
	
	public Layer( int numNeurons, Function f ) {
		this();
		initLayer( numNeurons, f );
	}
	
	public boolean addInputLink( LayerLink l ) {
		return incoming.add(l);
	}
	
	public boolean addOutputLink( LayerLink l ) {
		return outgoing.add(l);
	}
	
	public boolean addNeuron( Neuron n ) {
		return neurons.add(n);
	}
	
	public void backProp( double[] delta ) {
		for ( int i = 0; i < neurons.size(); i++ )
			neurons.get(i).backProp(delta[i]);
	}
	
	/**
	 * Feeds an arry of input values to the neurons in the layer. If the array is larger than
	 * the amount of neurons, the excess in the array is simply ignored. If the array is smaller,
	 * an ArrayIndexOutOfBoundsException will be thrown.
	 * 
	 * @param in an array of input values to be propagated
	 */
	public void feedForward( double[] in ) {
		for ( int i = 0; i < neurons.size(); i++ )
			neurons.get(i).feedForward(in[i]);
	}
	
	public double[] getActivation() {
		double[] act = new double[neurons.size()];
		for ( int i = 0; i < neurons.size(); i++ )
			act[i] = neurons.get(i).getActivation();
		return act;
	}
	
	public Neuron getNeuron( int i ) {
		return neurons.get(i);
	}
	
	public List<LayerLink> getIncoming() {
		return incoming;
	}
	
	public List<LayerLink> getOutgoing() {
		return outgoing;
	}
	
	public List<Neuron> getNeurons() {
		return neurons;
	}
	
	public double[] getWeights() {
		double[] weights = new double[ neurons.size() ];
		for ( int i = 0; i < neurons.size(); i++  )
			weights[i] = neurons.get(i).getWeight();
		return weights;
	}
	
	public void initLayer( int numNeurons, Function f ) {
		Neuron n;
		for( int i = 0; i < numNeurons; i++ ) {
			n = new Neuron(f);
			neurons.add(n);
		}
	}
	
	public void initWeights() {
		for ( Neuron n : neurons )
			n.initWeight();
	}
	
	public boolean removeInputLink( LayerLink l ) {
		return incoming.remove(l);
	}
	
	public boolean removeOutputLink( LayerLink l ) {
		return outgoing.remove(l);
	}
	
	public boolean removeNeuron( Neuron n ) {
		return neurons.remove(n);
	}
	
	public void setParameters( Parameters params ) {
		for ( Neuron n : neurons )
			n.setParameters(params);
	}
	
	public void setTransferFunction( Function f ) {
		for ( Neuron n : neurons )
			n.setTransferFunction(f);
	}
	
	public int size() {
		return neurons.size();
	}
}
